Skip to content

Auto-assign distinct colors to split panes - #6981

Closed
austinywang wants to merge 22 commits into
mainfrom
issue-5975-auto-assign-distinct-colors-to-split-panes-cr
Closed

austinywang wants to merge 22 commits into
mainfrom
issue-5975-auto-assign-distinct-colors-to-split-panes-cr

Conversation

@austinywang

@austinywang austinywang commented Jun 26, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #5975

Summary

  • auto-assign subtle distinct pane-local background tints when terminal splits are created
  • preserve existing pane colors, persist pane background colors in session snapshots, and cover remote tmux mirror panes
  • add terminal.autoTintSplitPanes to cmux.json schema, template, and localized docs messages

Validation

  • ./scripts/lint-pbxproj-test-wiring.sh
  • ./scripts/check-pbxproj.sh
  • git diff --check
  • parsed web/data/cmux.schema.json and all web/messages/*.json
  • verified terminal schema description keys are aligned across locales

No local dev build or xcodebuild run per issue instructions.


Summary by cubic

Auto-assigns subtle, distinct background tints to terminal split panes and restores them across sessions and remote tmux mirrors (fixes #5975). Known v1 limitation: reloading Ghostty config/themes clears pane tints until the next split re-assigns them.

  • New Features

    • Auto-tint source and new panes on split when they have no manual color; plan against the true terminal background to avoid tint-of-a-tint; keep colors distinct after palette wrap.
    • Persist and restore tints only when the live override still equals the cmux-assigned hex; ignore terminal/theme overrides; gate restore by terminal.autoTintSplitPanes so disabled stays disabled after restart.
    • Apply and re-apply tints for remote tmux mirror panes during layout reconcile using the real background color; add terminal.autoTintSplitPanes to settings (UserDefaults mapping, web/data/cmux.schema.json, template, and localized docs).
  • Migration

    • No action needed. To disable, set terminal.autoTintSplitPanes to false in cmux.json.

Written for commit 50a3d36. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added terminal.autoTintSplitPanes (default: enabled) to automatically apply subtle, distinct background tints to newly created terminal split panes.
    • Tint assignments now persist through session save/restore and stay consistent across restarts and mirrored remote split windows.
  • Bug Fixes
    • Improved coordinated tinting so restored/mirrored split panes remain visually consistent when tints are re-applied.
  • Documentation
    • Updated the configuration template, JSON schema, and localized help text for terminal.autoTintSplitPanes.

@vercel

vercel Bot commented Jun 26, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jul 5, 2026 9:31am
cmux-staging Building Building Preview, Comment Jul 5, 2026 9:31am

@coderabbitai

coderabbitai Bot commented Jun 26, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds automatic pane-level tinting for newly created terminal splits, with a new persisted setting, runtime and tmux wiring, snapshot persistence, settings-file support, and schema/docs/localization updates.

Changes

Auto-tint split panes feature

Layer / File(s) Summary
Settings key and runtime settings
Packages/macOS/CmuxSettings/Sources/CmuxSettings/Keys/TerminalCatalogSection.swift, Sources/App/WorkspaceRuntimeSettings.swift, Sources/SessionPersistence.swift
Adds the terminal.autoTintSplitPanes setting, reads it from defaults, and extends the session snapshot model with an optional backgroundColorHex field.
Settings file wiring and docs
Sources/CmuxSettingsJSONPathSupport.swift, Sources/KeyboardShortcutSettingsFileStore+Template.swift, web/data/cmux.schema.json, web/app/[locale]/docs/configuration/page.tsx, web/messages/*
Wires the setting into JSON path support and the generated template, and adds schema plus localized documentation strings.
Pane tint planning and application
Sources/GhosttyTerminalView.swift, Sources/Workspace.swift
Adds pane background override plumbing and the workspace tint planner/application flow for local split panes.
Session snapshot persistence
Sources/Workspace.swift, Sources/SessionPersistence.swift
Persists pane tint provenance in session snapshots and restores it when reopening a session.
Tmux mirror pane tinting
Sources/RemoteTmuxWindowMirror.swift, Sources/RemoteTmuxSessionMirror.swift
Passes tint defaults into tmux mirror reconciliation and applies automatic tints there as well.
Tests for tinting and persistence
cmuxTests/RemoteTmuxMirrorSplitRoutingTests.swift, cmuxTests/TerminalScrollSpeedSettingsFileStoreTests.swift
Adds coverage for tint selection, feature gating, snapshot round-tripping, and loading the new setting from cmux.json.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (2 errors, 2 warnings)

Check name Status Explanation Resolution
Cmux Architecture Rethink ❌ Error The patch reuses one shared background slot for both terminal and cmux tints and relies on the side-channel autoAssignedSplitTintHex, so provenance/bad states remain representable. Model background provenance explicitly (cmux vs terminal/theme) and thread it through surface setters, snapshot persistence, and restore instead of inferring from a matching hex.
Cmux No Ambient Global State ❌ Error New caseless enum TerminalSplitPaneTintPlanner is just a static namespace, which the rule forbids for production Swift changes. Move tint-planning behavior onto an injectable owning type or make it private/fileprivate helpers instead of a new namespace enum.
Docstring Coverage ⚠️ Warning Docstring coverage is 9.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The description covers summary and validation, but it misses required template sections like Demo Video, Review Trigger, and Checklist. Add the missing template sections: Testing, Demo Video, Review Trigger, and Checklist, and include the exact local verification and video/link details.
✅ Passed checks (21 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR implements automatic distinct split-pane tinting, preserves existing pane colors, supports disablement, and restores/applies tints across sessions and tmux.
Out of Scope Changes check ✅ Passed The diff stays focused on split-pane tinting, persistence, docs, tests, and settings wiring; no unrelated changes stand out.
Cmux Swift Actor Isolation ✅ Passed No new actor-isolation debt: the tint logic lives on already-@mainactor Workspace/RemoteTmuxWindowMirror types, and the new snapshot/settings additions are plain value types.
Cmux Swift Blocking Runtime ✅ Passed New tint code is pure/@mainactor; no new semaphores, sleeps, main-queue sync, or locks were added in production. Only short test-only RunLoop scaffolding appears.
Cmux Browser Automation Off-Main ✅ Passed Diff scope is terminal tint/settings/docs only; no changes in browser automation routing files or browser.* wait paths.
Cmux Expensive Synchronous Load ✅ Passed The diff only adds tint/settings and snapshot persistence; no new RestorableAgentSessionIndex.load() or other heavy agent-history load was moved onto an interactive/main-actor path.
Cmux Cache Substitution Correctness ✅ Passed PASS: the snapshot path guards persistence with a freshness check (live override must equal cmux-assigned tint) and documents the shared-slot limitation; no stale cache is blindly trusted.
Cmux No Hacky Sleeps ✅ Passed No introduced fixed sleeps/delays in changed TS/JS/shell runtime code; the diff only updates docs/config and shell flag wiring, with no timer-based coordination.
Cmux Algorithmic Complexity ✅ Passed PASS: new tint logic is linear over panes, and repeated scans are only over the fixed 8-color palette; no nested rescans or quadratic hot-path code was introduced.
Cmux Swift Concurrency ✅ Passed New tint/settings code is synchronous; no new DispatchQueue/Combine/completion-handler or fire-and-forget Task patterns were added beyond existing UI/callback boundaries.
Cmux Swift @Concurrent ✅ Passed No new nonisolated async/@Concurrent code; tinting helpers are synchronous and run on @MainActor in Workspace/RemoteTmuxWindowMirror.
Cmux Swift File And Package Boundaries ✅ Passed No new oversized files; Workspace.swift gained 179 lines of app-specific split/persistence glue, under the 250-line threshold and not clearly reusable domain logic.
Cmux Swiftpm Lockfiles ✅ Passed No Package.swift/.resolved/.gitignore/workflow changes; pbxproj diff only adds source/test wiring and has no SwiftPM package-reference edits.
Cmux Swift Logging ✅ Passed PASS: The PR diff adds no print/debugPrint/dump/NSLog/Logger in production Swift; existing logs in touched files are unchanged.
Cmux User-Facing Error Privacy ✅ Passed Touched files add settings/docs/tinting logic only; no new user-facing errors, alerts, API bodies, or recovery copy expose sensitive upstream details.
Cmux Full Internationalization ✅ Passed PASS: web/i18n/routing.ts has 20 locales, web/messages/*.json covers all 20 with non-empty autoTintSplitPanes entries, and no xcstrings/Info.plist files were touched.
Cmux Swiftui State Layout ✅ Passed PASS: Diff adds only @State/@AppStorage in standalone views and AppKit bridges; no new ObservableObject/@published or layout-changing GeometryReader additions.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PASS: the PR only touches an existing settings WindowGroup with stable id cmux.settings; no new standalone window or close-shortcut ownership change appears.
Cmux Source Artifacts ✅ Passed All changed paths are intentional source/docs/localization/config files; no temp/build/cache/log/screenshot/artifact paths were added. The TSV is a checked-in CI budget config.
Cmux No Test Or Debug Seam In Production Source ✅ Passed PASS: The new tint accessors/settings are used by production code paths, and the diff adds no new #if DEBUG/ForTesting/debug... seams in Sources/.
Title check ✅ Passed The title is concise and clearly summarizes the main feature change: automatic distinct split-pane colors.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-5975-auto-assign-distinct-colors-to-split-panes-cr

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

cmux added 2 commits June 26, 2026 16:40
…n-distinct-colors-to-split-panes-cr

# Conflicts:
#	.github/swift-file-length-budget.tsv
@greptile-apps

greptile-apps Bot commented Jun 26, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR introduces terminal.autoTintSplitPanes — a setting (default on) that auto-assigns subtle, distinct background tints to terminal split panes, persists them in session snapshots, and re-applies them for remote tmux mirror panes during layout reconcile.

  • Core tinting logic (TerminalSplitPaneTintPlanner) blends an 8-color palette into the true terminal background at low opacity, tracks assigned hexes to keep splits distinct, wraps deterministically after palette exhaustion, and guards against tint-of-a-tint drift by always planning against the real background color — not an already-tinted source pane.
  • Persistence is gated on cmux provenance: only a live paneBackgroundOverrideColor that still equals the auto-assigned hex is written to SessionTerminalPanelSnapshot.backgroundColorHex, preventing terminal OSC/config-reload background changes from being resurrected as sticky tints on next restore.
  • Localization and schema are updated across all 18 supported locales and cmux.schema.json; the terminal.autoTintSplitPanes key is wired through CmuxSettingsFileStore, KeyboardShortcutSettingsFileStore+Template, and CmuxSettingsJSONPathSupport.

Confidence Score: 5/5

Safe to merge — the feature is well-isolated, fully togglable, and the persistence provenance guard correctly prevents terminal-controlled backgrounds from leaking into session restore as cmux-assigned tints.

The tinting logic is pure computation with no mutable global state risk, the session snapshot field is optional and backward-compatible, and 11 targeted unit tests cover the planner's assignment, wrap-around, and provenance-guard behavior. The one architectural note (TerminalSplitPaneTintPlanner as a caseless enum namespace) is a style concern matching a known team rule, not a runtime defect.

Sources/Workspace.swift — contains the caseless-enum namespace and the unused static forwarder worth a follow-up cleanup.

Important Files Changed

Filename Overview
Sources/Workspace.swift Adds TerminalSplitPaneTintPlanner (caseless enum namespace — ambient global state rule violation), TerminalSplitPaneTintAssignment, applyAutomaticSplitPaneTints, and session snapshot/restore plumbing for pane tints; one unused static forwarder (automaticSplitPaneTintColor).
Sources/RemoteTmuxWindowMirror.swift Injects paneTintDefaults UserDefaults and calls applyAutomaticPaneTints after every layout reconcile; correctly uses GhosttyApp.shared.defaultBackgroundColor (same source as the local path).
Sources/GhosttyTerminalView.swift Exposes paneBackgroundOverrideColor and autoAssignedSplitTintHex accessors on TerminalSurface and GhosttySurfaceScrollView for tint assignment and session-persistence provenance tracking.
Sources/SessionPersistence.swift Adds optional backgroundColorHex field to SessionTerminalPanelSnapshot for persisting cmux-owned split tints across restarts; backward-compatible (optional, nil by default).
Sources/App/WorkspaceRuntimeSettings.swift Adds TerminalSplitPaneTintSettings as an injectable struct with instance isEnabled(defaults:) method; still exposes static key/default accessors used by template and mapping files.
Sources/RemoteTmuxSessionMirror.swift Threads workspace.terminalSplitPaneTintDefaults through to RemoteTmuxWindowMirror init for DI; one-line mechanical change.
web/data/cmux.schema.json Adds autoTintSplitPanes boolean (default true) to the terminal schema section with a descriptionKey for localized rendering.
cmuxTests/RemoteTmuxMirrorSplitRoutingTests.swift Adds 11 new tests covering planner assignment, palette exhaustion, wrap-around distinctness, persistableTintHex provenance guards, session snapshot round-trip, and the true-base vs tinted-source regression; well-isolated using ephemeral UserDefaults suites.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[newTerminalSplit called] --> B[applyAutomaticSplitPaneTints]
    B --> C{autoTintSplitPanes enabled?}
    C -- No --> Z[done]
    C -- Yes --> D[Resolve true background\nWorkspaceContentView.resolveGhosttyAppearanceConfig]
    D --> E[Collect usedHexes\nfrom all pane overrides]
    E --> F[TerminalSplitPaneTintPlanner\n.assignmentForTerminalSplit]
    F --> G{source needs tint?}
    G -- Yes --> H[nextColor → sourceColor\nadd to usedHexes + selectedHexes]
    G -- No --> I[sourceColor = nil]
    H --> J{newPane needs tint?}
    I --> J
    J -- Yes --> K[nextColor excluding selectedHexes\n→ newPaneColor]
    J -- No --> L[newPaneColor = nil]
    K --> M[Apply colors\nset autoAssignedSplitTintHex]
    L --> M

    N[Session snapshot] --> O[persistableTintHex\nliveOverride == autoAssigned?]
    O -- Yes --> P[Write backgroundColorHex]
    O -- No --> Q[Skip - terminal-controlled]

    R[Session restore] --> S{autoTintSplitPanes enabled?\n& backgroundColorHex present?}
    S -- Yes --> T[Restore paneBackgroundOverrideColor\nRe-record autoAssignedSplitTintHex]
    S -- No --> U[Skip restore]

    V[RemoteTmuxWindowMirror\nreconcile] --> W[applyAutomaticPaneTints]
    W --> X[Assign tints to untinted\nremote panes in order]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[newTerminalSplit called] --> B[applyAutomaticSplitPaneTints]
    B --> C{autoTintSplitPanes enabled?}
    C -- No --> Z[done]
    C -- Yes --> D[Resolve true background\nWorkspaceContentView.resolveGhosttyAppearanceConfig]
    D --> E[Collect usedHexes\nfrom all pane overrides]
    E --> F[TerminalSplitPaneTintPlanner\n.assignmentForTerminalSplit]
    F --> G{source needs tint?}
    G -- Yes --> H[nextColor → sourceColor\nadd to usedHexes + selectedHexes]
    G -- No --> I[sourceColor = nil]
    H --> J{newPane needs tint?}
    I --> J
    J -- Yes --> K[nextColor excluding selectedHexes\n→ newPaneColor]
    J -- No --> L[newPaneColor = nil]
    K --> M[Apply colors\nset autoAssignedSplitTintHex]
    L --> M

    N[Session snapshot] --> O[persistableTintHex\nliveOverride == autoAssigned?]
    O -- Yes --> P[Write backgroundColorHex]
    O -- No --> Q[Skip - terminal-controlled]

    R[Session restore] --> S{autoTintSplitPanes enabled?\n& backgroundColorHex present?}
    S -- Yes --> T[Restore paneBackgroundOverrideColor\nRe-record autoAssignedSplitTintHex]
    S -- No --> U[Skip restore]

    V[RemoteTmuxWindowMirror\nreconcile] --> W[applyAutomaticPaneTints]
    W --> X[Assign tints to untinted\nremote panes in order]
Loading

Reviews (18): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile

Comment thread Sources/App/WorkspaceRuntimeSettings.swift Outdated
cmux added 3 commits June 28, 2026 20:21
…n-distinct-colors-to-split-panes-cr

# Conflicts:
#	.github/swift-file-length-budget.tsv
#	Sources/Workspace.swift
…n-distinct-colors-to-split-panes-cr

# Conflicts:
#	.github/swift-file-length-budget.tsv
…n-distinct-colors-to-split-panes-cr

# Conflicts:
#	.github/swift-file-length-budget.tsv

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmuxTests/RemoteTmuxMirrorSplitRoutingTests.swift`:
- Around line 145-148: The current test only checks that
SessionTerminalPanelSnapshot stores backgroundColorHex in memory, so it does not
verify persistence behavior. Update terminalSnapshotPersistsPaneBackgroundHex to
exercise a full snapshot round-trip by encoding and decoding the snapshot
through its Codable path, then assert the restored SessionTerminalPanelSnapshot
still has the same backgroundColorHex. Use the existing
SessionTerminalPanelSnapshot initializer and verify the field survives via
CodingKeys/restore logic rather than only the direct initializer.

In `@Sources/App/WorkspaceRuntimeSettings.swift`:
- Around line 202-210: The `TerminalSplitPaneTintSettings` helper is duplicating
the auto-tint split panes key and default instead of reusing the catalog’s
`DefaultsKey` source of truth. Update `isEnabled(defaults:)` to read the key and
default from `TerminalCatalogSection.autoTintSplitPanes` (the same catalog value
used by `CmuxSettingsJSONPathSupport.swift`) so the runtime setting stays in
sync with the settings catalog and import path.

In `@Sources/Workspace.swift`:
- Around line 7087-7100: The split-pane tint logic in
applyAutomaticSplitPaneTints is using the app-wide default background instead of
the source pane’s actual effective terminal background. Update baseColor to come
from the source TerminalPanel’s resolved background (via terminalPanel(for:) and
its surface/background config) so assignmentForTerminalSplit tints against the
pane’s real color, preserving per-pane themes and correct opacity selection.
- Around line 88-96: The tint selection in Workspace’s split-pane flow can reuse
the same wrapped palette color for both panes because `usedHexes` only tracks
historical colors and `nextColor(baseColor:usedHexes:)` is called twice. Update
the `sourceColor`/`newPaneColor` assignment path to track the color chosen in
the current operation separately, and make the second `nextColor` call exclude
the already selected tint so both panes never receive the same color.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3c612c29-0813-49e0-a987-2a4330c0f11b

📥 Commits

Reviewing files that changed from the base of the PR and between 5265559 and 387049d.

⛔ Files ignored due to path filters (1)
  • .github/swift-file-length-budget.tsv is excluded by !**/*.tsv
📒 Files selected for processing (33)
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Keys/TerminalCatalogSection.swift
  • Sources/App/WorkspaceRuntimeSettings.swift
  • Sources/CmuxSettingsJSONPathSupport.swift
  • Sources/GhosttyTerminalView.swift
  • Sources/KeyboardShortcutSettingsFileStore+Template.swift
  • Sources/RemoteTmuxSessionMirror.swift
  • Sources/RemoteTmuxWindowMirror.swift
  • Sources/SessionPersistence.swift
  • Sources/Workspace.swift
  • cmuxTests/RemoteTmuxMirrorSplitRoutingTests.swift
  • cmuxTests/TerminalScrollSpeedSettingsFileStoreTests.swift
  • web/app/[locale]/docs/configuration/page.tsx
  • web/data/cmux.schema.json
  • web/messages/ar.json
  • web/messages/bs.json
  • web/messages/da.json
  • web/messages/de.json
  • web/messages/en.json
  • web/messages/es.json
  • web/messages/fr.json
  • web/messages/it.json
  • web/messages/ja.json
  • web/messages/km.json
  • web/messages/ko.json
  • web/messages/no.json
  • web/messages/pl.json
  • web/messages/pt-BR.json
  • web/messages/ru.json
  • web/messages/th.json
  • web/messages/tr.json
  • web/messages/uk.json
  • web/messages/zh-CN.json
  • web/messages/zh-TW.json

Comment thread cmuxTests/RemoteTmuxMirrorSplitRoutingTests.swift Outdated
Comment thread Sources/App/WorkspaceRuntimeSettings.swift Outdated
Comment thread Sources/Workspace.swift
Comment thread Sources/Workspace.swift
austinywang and others added 2 commits June 29, 2026 16:34
…n-distinct-colors-to-split-panes-cr

# Conflicts:
#	.github/swift-file-length-budget.tsv
Add Swift-DocC documentation to the newly introduced public
autoTintSplitPanes DefaultsKey, matching the documentation pattern used
by sibling catalog keys (e.g. rendererRealizationEnabled).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…n-distinct-colors-to-split-panes-cr

# Conflicts:
#	.github/swift-file-length-budget.tsv
austinywang and others added 2 commits July 2, 2026 01:53
…n-distinct-colors-to-split-panes-cr

# Conflicts:
#	.github/swift-file-length-budget.tsv
Auto split-pane tints are stored in the surface's paneBackgroundOverrideColor
slot (shared with Ghostty OSC/config background overrides), so a config/theme
reload clears them until the next split. Recorded as an accepted v1 limitation
per PR review rather than reworking the shared-slot storage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@austinywang

austinywang commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor Author

Autoreview note — accepted v1 limitation

Automatic split-pane tints are stored in the surface's paneBackgroundOverrideColor slot, shared with Ghostty's OSC/config background overrides and carrying no provenance metadata. That single design choice has three P2 consequences flagged across autoreview passes:

  1. A Ghostty config/theme reload (GHOSTTY_ACTION_CONFIG_CHANGE) clears the slot, so tints reset until the next split re-assigns them.
  2. A transient OSC background set by a terminal program can be persisted into a session snapshot as if it were a cmux tint.
  3. Disabling terminal.autoTintSplitPanes does not clear already-persisted tints on restore (the opt-out only affects newly created panes).

All verified real, but accepted as a v1 limitation rather than reworking the shared-slot storage now: a correct fix is source-tagging backgroundColor across the OSC handler, the config-change clear, the setter, and snapshot persist/restore — a multi-site change to the typing-latency-sensitive render path, better done as a follow-up. Documented in code at applyAutomaticSplitPaneTints (Workspace.swift).

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 34 files

Re-trigger cubic

`nextColor` is fed the set of distinct colors already used by live
panes, so `usedHexes` saturates at the eight-color palette: once all
eight colors are present the Set can no longer grow, and the
`usedHexes.count % palette.count` wrap resolves to index 0 for every
subsequent request. The test asserted clean modular cycling
(`tints[9] == tints[1]`), which the planner never produces from a
saturating Set. Correct it to `tints[9] == tints[0]`, rename the test,
and document why, matching the real call site's behavior.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
austinywang and others added 3 commits July 2, 2026 04:15
The auto-tint feature stored assigned colors in `paneBackgroundOverrideColor`,
a surface slot shared with terminal OSC background changes
(GHOSTTY_ACTION_COLOR_CHANGE) and Ghostty config reloads
(GHOSTTY_ACTION_CONFIG_CHANGE). Session persistence snapshotted and restored
that slot unconditionally, so:

  * a transient terminal/OSC background present at save time was persisted and
    resurrected later as a sticky cmux pane tint, overriding the user's theme;
  * restore reapplied tints even when `terminal.autoTintSplitPanes` was
    disabled, so the opt-out did not survive a restart.

Record the cmux-assigned hex as provenance on the surface
(`autoAssignedSplitTintHex`) at every assignment site (split, remote-tmux
mirror, and restore). Persist `backgroundColorHex` only when the live override
still equals that provenance -- a terminal overwrite or config-reload clear
makes them diverge and drops the value -- and gate restore on the
`terminal.autoTintSplitPanes` setting so a disabled feature is not resurrected.

Adds pure regression coverage for the persistence gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… tint

The local split path fed the source pane's live paneBackgroundOverrideColor
into TerminalSplitPaneTintPlanner as the base color. The planner blends its
palette *into* the base, so once a source pane was tinted, planning the next
split against that tinted color generated off-palette tint-of-a-tint colors;
repeated splits from the same pane drifted/compounded instead of cycling the
distinct palette (PR #6981 autoreview). Always use the resolved Ghostty
background as the base, matching RemoteTmuxWindowMirror.applyAutomaticPaneTints;
existing overrides remain represented via usedHexes and sourceNeedsTint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…n-distinct-colors-to-split-panes-cr

# Conflicts:
#	.github/swift-file-length-budget.tsv
…n-distinct-colors-to-split-panes-cr

Resolve .github/swift-file-length-budget.tsv via scripts/swift_file_length_budget.py --write-budget (only textual conflict; numeric drift on both sides).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Sources/GhosttyTerminalView.swift (1)

8049-8066: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Shared background slot mixes cmux tint provenance with OSC/config state.

paneBackgroundOverrideColor is the same NSColor? slot mutated by OSC 11 background changes (GHOSTTY_ACTION_COLOR_CHANGE) and cleared by Ghostty config reloads (GHOSTTY_ACTION_CONFIG_CHANGE) elsewhere in this file. autoAssignedSplitTintHex is only a "last assigned" marker, not real provenance tagging, so a transient OSC background write or a config-reload clear can desync it from the actual backgroundColor, and a stale autoAssignedSplitTintHex can be persisted into session snapshots as if it were a genuine cmux tint.

This is the exact scenario called out by the single-source-of-truth guideline for pane tint provenance. Per the PR's own comments, this is an acknowledged v1 limitation (fixing it needs source-tagging backgroundColor across OSC/config/setter/persistence paths) accepted for now as a documented follow-up, so I'm not blocking on it — just flagging so the follow-up tracking isn't lost.

Based on the coding guideline requiring "pane background tint provenance ... come from the stored cmux-owned fields ... not from string/title matching or 'best effort' branches," and the PR objectives, which state this tradeoff is accepted for v1 pending a follow-up.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/GhosttyTerminalView.swift` around lines 8049 - 8066, The split-pane
tint provenance is still derived from the shared `paneBackgroundOverrideColor`
slot, which can be overwritten by OSC 11 and config reloads, so
`autoAssignedSplitTintHex` can drift from the actual background color. Update
the persistence logic around `TerminalSplitPaneTintPlanner.persistableTintHex`
and the `paneBackgroundOverrideColor`/`autoAssignedSplitTintHex` fields so only
cmux-owned tint state is persisted, or otherwise add explicit source tagging to
distinguish cmux tint from OSC/config writes. If this is intentionally deferred
for v1, keep the limitation clearly documented in the relevant symbols so the
follow-up is tracked.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@Sources/GhosttyTerminalView.swift`:
- Around line 8049-8066: The split-pane tint provenance is still derived from
the shared `paneBackgroundOverrideColor` slot, which can be overwritten by OSC
11 and config reloads, so `autoAssignedSplitTintHex` can drift from the actual
background color. Update the persistence logic around
`TerminalSplitPaneTintPlanner.persistableTintHex` and the
`paneBackgroundOverrideColor`/`autoAssignedSplitTintHex` fields so only
cmux-owned tint state is persisted, or otherwise add explicit source tagging to
distinguish cmux tint from OSC/config writes. If this is intentionally deferred
for v1, keep the limitation clearly documented in the relevant symbols so the
follow-up is tracked.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4ba6b525-af2d-47fe-9504-96ab25b86b71

📥 Commits

Reviewing files that changed from the base of the PR and between 6095b56 and 50a3d36.

⛔ Files ignored due to path filters (1)
  • .github/swift-file-length-budget.tsv is excluded by !**/*.tsv
📒 Files selected for processing (4)
  • Sources/CmuxSettingsJSONPathSupport.swift
  • Sources/GhosttyTerminalView.swift
  • Sources/Workspace.swift
  • web/app/[locale]/docs/configuration/page.tsx
💤 Files with no reviewable changes (2)
  • web/app/[locale]/docs/configuration/page.tsx
  • Sources/Workspace.swift

This branch was successfully deployed

1 active deployment
Preview – cmux — 50a3d369 Deployed Jul 5, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale-revisit Closed after 30+ days without activity; preserved for possible revisit or reopening.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auto-assign distinct colors to split panes created with Cmd+D

3 participants